// Copyright 2018 ETH Zurich and University of Bologna.
// Copyright and related rights are licensed under the Solderpad Hardware
// License, Version 0.51 (the "License"); you may not use this file except in
// compliance with the License.  You may obtain a copy of the License at
// http://solderpad.org/licenses/SHL-0.51. Unless required by applicable law
// or agreed to in writing, software, hardware and materials distributed under
// this License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
// specific language governing permissions and limitations under the License.

module apb_adv_timer #(
	parameter APB_ADDR_WIDTH = 12,
	parameter EXTSIG_NUM     = 32,
	parameter TIMER_NBITS    = 16
) (
    input  logic                      HCLK,
    input  logic                      HRESETn,
    input  logic [APB_ADDR_WIDTH-1:0] PADDR,
    input  logic               [31:0] PWDATA,
    input  logic                      PWRITE,
    input  logic                      PSEL,
    input  logic                      PENABLE,
    output logic               [31:0] PRDATA,
    output logic                      PREADY,
    output logic                      PSLVERR,

	input  logic                      dft_cg_enable_i,
	input  logic                      low_speed_clk_i,
	input  logic     [EXTSIG_NUM-1:0] ext_sig_i,

	output logic                [3:0] events_o,	

	output logic                [3:0] ch_0_o,	
	output logic                [3:0] ch_1_o,	
	output logic                [3:0] ch_2_o,	
	output logic                [3:0] ch_3_o
);

	localparam N_TIMEREXTSIG = EXTSIG_NUM+16;

    logic                      s_timer0_apb_start;
    logic                      s_timer0_apb_stop;
	logic                      s_timer0_apb_arm;
	logic                      s_timer0_apb_update;
	logic                      s_timer0_apb_rst;
	logic                      s_timer0_apb_saw;
	logic                [7:0] s_timer0_apb_in_sel;
	logic                [2:0] s_timer0_apb_in_mode;
	logic                [7:0] s_timer0_apb_presc;
	logic               [15:0] s_timer0_apb_th_hi;
	logic               [15:0] s_timer0_apb_th_low;
	logic                [2:0] s_timer0_apb_ch0_mode;
	logic                [1:0] s_timer0_apb_ch0_flt;
	logic               [15:0] s_timer0_apb_ch0_th;
	logic               [15:0] s_timer0_apb_ch0_lut;
	logic                [2:0] s_timer0_apb_ch1_mode;
	logic                [1:0] s_timer0_apb_ch1_flt;
	logic               [15:0] s_timer0_apb_ch1_th;
	logic               [15:0] s_timer0_apb_ch1_lut;
	logic                [2:0] s_timer0_apb_ch2_mode;
	logic                [1:0] s_timer0_apb_ch2_flt;
	logic               [15:0] s_timer0_apb_ch2_th;
	logic               [15:0] s_timer0_apb_ch2_lut;
	logic                [2:0] s_timer0_apb_ch3_mode;
	logic                [1:0] s_timer0_apb_ch3_flt;
	logic               [15:0] s_timer0_apb_ch3_th;
	logic               [15:0] s_timer0_apb_ch3_lut;

    logic                      s_timer1_apb_start;
    logic                      s_timer1_apb_stop;
	logic                      s_timer1_apb_arm;
	logic                      s_timer1_apb_update;
	logic                      s_timer1_apb_rst;
	logic                      s_timer1_apb_saw;
	logic                [7:0] s_timer1_apb_in_sel;
	logic                [2:0] s_timer1_apb_in_mode;
	logic                [7:0] s_timer1_apb_presc;
	logic               [15:0] s_timer1_apb_th_hi;
	logic               [15:0] s_timer1_apb_th_low;
	logic                [2:0] s_timer1_apb_ch0_mode;
	logic                [1:0] s_timer1_apb_ch0_flt;
	logic               [15:0] s_timer1_apb_ch0_th;
	logic               [15:0] s_timer1_apb_ch0_lut;
	logic                [2:0] s_timer1_apb_ch1_mode;
	logic                [1:0] s_timer1_apb_ch1_flt;
	logic               [15:0] s_timer1_apb_ch1_th;
	logic               [15:0] s_timer1_apb_ch1_lut;
	logic                [2:0] s_timer1_apb_ch2_mode;
	logic                [1:0] s_timer1_apb_ch2_flt;
	logic               [15:0] s_timer1_apb_ch2_th;
	logic               [15:0] s_timer1_apb_ch2_lut;
	logic                [2:0] s_timer1_apb_ch3_mode;
	logic                [1:0] s_timer1_apb_ch3_flt;
	logic               [15:0] s_timer1_apb_ch3_th;
	logic               [15:0] s_timer1_apb_ch3_lut;

    logic                      s_timer2_apb_start;
    logic                      s_timer2_apb_stop;
	logic                      s_timer2_apb_arm;
	logic                      s_timer2_apb_update;
	logic                      s_timer2_apb_rst;
	logic                      s_timer2_apb_saw;
	logic                [7:0] s_timer2_apb_in_sel;
	logic                [2:0] s_timer2_apb_in_mode;
	logic                [7:0] s_timer2_apb_presc;
	logic               [15:0] s_timer2_apb_th_hi;
	logic               [15:0] s_timer2_apb_th_low;
	logic                [2:0] s_timer2_apb_ch0_mode;
	logic                [1:0] s_timer2_apb_ch0_flt;
	logic               [15:0] s_timer2_apb_ch0_th;
	logic               [15:0] s_timer2_apb_ch0_lut;
	logic                [2:0] s_timer2_apb_ch1_mode;
	logic                [1:0] s_timer2_apb_ch1_flt;
	logic               [15:0] s_timer2_apb_ch1_th;
	logic               [15:0] s_timer2_apb_ch1_lut;
	logic                [2:0] s_timer2_apb_ch2_mode;
	logic                [1:0] s_timer2_apb_ch2_flt;
	logic               [15:0] s_timer2_apb_ch2_th;
	logic               [15:0] s_timer2_apb_ch2_lut;
	logic                [2:0] s_timer2_apb_ch3_mode;
	logic                [1:0] s_timer2_apb_ch3_flt;
	logic               [15:0] s_timer2_apb_ch3_th;
	logic               [15:0] s_timer2_apb_ch3_lut;

    logic                      s_timer3_apb_start;
    logic                      s_timer3_apb_stop;
	logic                      s_timer3_apb_arm;
	logic                      s_timer3_apb_update;
	logic                      s_timer3_apb_rst;
	logic                      s_timer3_apb_saw;
	logic                [7:0] s_timer3_apb_in_sel;
	logic                [2:0] s_timer3_apb_in_mode;
	logic                [7:0] s_timer3_apb_presc;
	logic               [15:0] s_timer3_apb_th_hi;
	logic               [15:0] s_timer3_apb_th_low;
	logic                [2:0] s_timer3_apb_ch0_mode;
	logic                [1:0] s_timer3_apb_ch0_flt;
	logic               [15:0] s_timer3_apb_ch0_th;
	logic               [15:0] s_timer3_apb_ch0_lut;
	logic                [2:0] s_timer3_apb_ch1_mode;
	logic                [1:0] s_timer3_apb_ch1_flt;
	logic               [15:0] s_timer3_apb_ch1_th;
	logic               [15:0] s_timer3_apb_ch1_lut;
	logic                [2:0] s_timer3_apb_ch2_mode;
	logic                [1:0] s_timer3_apb_ch2_flt;
	logic               [15:0] s_timer3_apb_ch2_th;
	logic               [15:0] s_timer3_apb_ch2_lut;
	logic                [2:0] s_timer3_apb_ch3_mode;
	logic                [1:0] s_timer3_apb_ch3_flt;
	logic               [15:0] s_timer3_apb_ch3_th;
	logic               [15:0] s_timer3_apb_ch3_lut;

	logic             [3:0] s_event_en;
	logic             [3:0] s_event_sel_0;
	logic             [3:0] s_event_sel_1;
	logic             [3:0] s_event_sel_2;
	logic             [3:0] s_event_sel_3;
	logic        s_timer0_apb_in_clk;
	logic        s_timer1_apb_in_clk;
	logic        s_timer2_apb_in_clk;
	logic        s_timer3_apb_in_clk;

	logic [15:0] s_timer0_counter;
	logic [15:0] s_timer1_counter;
	logic [15:0] s_timer2_counter;
	logic [15:0] s_timer3_counter;


    logic        s_timer0_clk_en;
    logic        s_timer1_clk_en;
    logic        s_timer2_clk_en;
    logic        s_timer3_clk_en;

    logic        s_clk_timer0;
    logic        s_clk_timer1;
    logic        s_clk_timer2;
    logic        s_clk_timer3;


	adv_timer_apb_if #(
      	.APB_ADDR_WIDTH   ( APB_ADDR_WIDTH         )
   	) u_apb_if (
    	.HCLK                    ( HCLK                 ),
    	.HRESETn                 ( HRESETn              ),
    	.PADDR                   ( PADDR                ),
    	.PWDATA                  ( PWDATA               ),
    	.PWRITE                  ( PWRITE               ),
    	.PSEL                    ( PSEL                 ),
    	.PENABLE                 ( PENABLE              ),
    	.PRDATA                  ( PRDATA               ),
    	.PREADY                  ( PREADY               ),
    	.PSLVERR                 ( PSLVERR              ),

    	.events_en_o             ( s_event_en           ),
    	.events_sel_0_o          ( s_event_sel_0        ),
    	.events_sel_1_o          ( s_event_sel_1        ),
    	.events_sel_2_o          ( s_event_sel_2        ),
    	.events_sel_3_o          ( s_event_sel_3        ),

    	.timer0_start_o          ( s_timer0_apb_start    ),
    	.timer0_stop_o           ( s_timer0_apb_stop     ),
		.timer0_arm_o            ( s_timer0_apb_arm      ),
		.timer0_update_o         ( s_timer0_apb_update   ),
		.timer0_rst_o            ( s_timer0_apb_rst      ),
		.timer0_saw_o            ( s_timer0_apb_saw      ),
		.timer0_in_sel_o         ( s_timer0_apb_in_sel   ),
		.timer0_in_clk_o         ( s_timer0_apb_in_clk   ),
		.timer0_in_mode_o        ( s_timer0_apb_in_mode  ),
		.timer0_presc_o          ( s_timer0_apb_presc    ),
		.timer0_th_hi_o          ( s_timer0_apb_th_hi    ),
		.timer0_th_low_o         ( s_timer0_apb_th_low   ),
		.timer0_ch0_mode_o       ( s_timer0_apb_ch0_mode ),
		.timer0_ch0_flt_o        ( s_timer0_apb_ch0_flt  ),
		.timer0_ch0_th_o         ( s_timer0_apb_ch0_th   ),
		.timer0_ch0_lut_o        ( s_timer0_apb_ch0_lut  ),
		.timer0_ch1_mode_o       ( s_timer0_apb_ch1_mode ),
		.timer0_ch1_flt_o        ( s_timer0_apb_ch1_flt  ),
		.timer0_ch1_th_o         ( s_timer0_apb_ch1_th   ),
		.timer0_ch1_lut_o        ( s_timer0_apb_ch1_lut  ),
		.timer0_ch2_mode_o       ( s_timer0_apb_ch2_mode ),
		.timer0_ch2_flt_o        ( s_timer0_apb_ch2_flt  ),
		.timer0_ch2_th_o         ( s_timer0_apb_ch2_th   ),
		.timer0_ch2_lut_o        ( s_timer0_apb_ch2_lut  ),
		.timer0_ch3_mode_o       ( s_timer0_apb_ch3_mode ),
		.timer0_ch3_flt_o        ( s_timer0_apb_ch3_flt  ),
		.timer0_ch3_th_o         ( s_timer0_apb_ch3_th   ),
		.timer0_ch3_lut_o        ( s_timer0_apb_ch3_lut  ),
		.timer0_counter_i        ( s_timer0_counter      ),

    	.timer1_start_o          ( s_timer1_apb_start    ),
    	.timer1_stop_o           ( s_timer1_apb_stop     ),
		.timer1_arm_o            ( s_timer1_apb_arm      ),
		.timer1_update_o         ( s_timer1_apb_update   ),
		.timer1_rst_o            ( s_timer1_apb_rst      ),
		.timer1_saw_o            ( s_timer1_apb_saw      ),
		.timer1_in_sel_o         ( s_timer1_apb_in_sel   ),
		.timer1_in_clk_o         ( s_timer1_apb_in_clk   ),
		.timer1_in_mode_o        ( s_timer1_apb_in_mode  ),
		.timer1_presc_o          ( s_timer1_apb_presc    ),
		.timer1_th_hi_o          ( s_timer1_apb_th_hi    ),
		.timer1_th_low_o         ( s_timer1_apb_th_low   ),
		.timer1_ch0_mode_o       ( s_timer1_apb_ch0_mode ),
		.timer1_ch0_flt_o        ( s_timer1_apb_ch0_flt  ),
		.timer1_ch0_th_o         ( s_timer1_apb_ch0_th   ),
		.timer1_ch0_lut_o        ( s_timer1_apb_ch0_lut  ),
		.timer1_ch1_mode_o       ( s_timer1_apb_ch1_mode ),
		.timer1_ch1_flt_o        ( s_timer1_apb_ch1_flt  ),
		.timer1_ch1_th_o         ( s_timer1_apb_ch1_th   ),
		.timer1_ch1_lut_o        ( s_timer1_apb_ch1_lut  ),
		.timer1_ch2_mode_o       ( s_timer1_apb_ch2_mode ),
		.timer1_ch2_flt_o        ( s_timer1_apb_ch2_flt  ),
		.timer1_ch2_th_o         ( s_timer1_apb_ch2_th   ),
		.timer1_ch2_lut_o        ( s_timer1_apb_ch2_lut  ),
		.timer1_ch3_mode_o       ( s_timer1_apb_ch3_mode ),
		.timer1_ch3_flt_o        ( s_timer1_apb_ch3_flt  ),
		.timer1_ch3_th_o         ( s_timer1_apb_ch3_th   ),
		.timer1_ch3_lut_o        ( s_timer1_apb_ch3_lut  ),
		.timer1_counter_i        ( s_timer1_counter      ),

    	.timer2_start_o          ( s_timer2_apb_start    ),
    	.timer2_stop_o           ( s_timer2_apb_stop     ),
		.timer2_arm_o            ( s_timer2_apb_arm      ),
		.timer2_update_o         ( s_timer2_apb_update   ),
		.timer2_rst_o            ( s_timer2_apb_rst      ),
		.timer2_saw_o            ( s_timer2_apb_saw      ),
		.timer2_in_sel_o         ( s_timer2_apb_in_sel   ),
		.timer2_in_clk_o         ( s_timer2_apb_in_clk   ),
		.timer2_in_mode_o        ( s_timer2_apb_in_mode  ),
		.timer2_presc_o          ( s_timer2_apb_presc    ),
		.timer2_th_hi_o          ( s_timer2_apb_th_hi    ),
		.timer2_th_low_o         ( s_timer2_apb_th_low   ),
		.timer2_ch0_mode_o       ( s_timer2_apb_ch0_mode ),
		.timer2_ch0_flt_o        ( s_timer2_apb_ch0_flt  ),
		.timer2_ch0_th_o         ( s_timer2_apb_ch0_th   ),
		.timer2_ch0_lut_o        ( s_timer2_apb_ch0_lut  ),
		.timer2_ch1_mode_o       ( s_timer2_apb_ch1_mode ),
		.timer2_ch1_flt_o        ( s_timer2_apb_ch1_flt  ),
		.timer2_ch1_th_o         ( s_timer2_apb_ch1_th   ),
		.timer2_ch1_lut_o        ( s_timer2_apb_ch1_lut  ),
		.timer2_ch2_mode_o       ( s_timer2_apb_ch2_mode ),
		.timer2_ch2_flt_o        ( s_timer2_apb_ch2_flt  ),
		.timer2_ch2_th_o         ( s_timer2_apb_ch2_th   ),
		.timer2_ch2_lut_o        ( s_timer2_apb_ch2_lut  ),
		.timer2_ch3_mode_o       ( s_timer2_apb_ch3_mode ),
		.timer2_ch3_flt_o        ( s_timer2_apb_ch3_flt  ),
		.timer2_ch3_th_o         ( s_timer2_apb_ch3_th   ),
		.timer2_ch3_lut_o        ( s_timer2_apb_ch3_lut  ),
		.timer2_counter_i        ( s_timer2_counter      ),

    	.timer3_start_o          ( s_timer3_apb_start    ),
    	.timer3_stop_o           ( s_timer3_apb_stop     ),
		.timer3_arm_o            ( s_timer3_apb_arm      ),
		.timer3_update_o         ( s_timer3_apb_update   ),
		.timer3_rst_o            ( s_timer3_apb_rst      ),
		.timer3_saw_o            ( s_timer3_apb_saw      ),
		.timer3_in_sel_o         ( s_timer3_apb_in_sel   ),
		.timer3_in_clk_o         ( s_timer3_apb_in_clk   ),
		.timer3_in_mode_o        ( s_timer3_apb_in_mode  ),
		.timer3_presc_o          ( s_timer3_apb_presc    ),
		.timer3_th_hi_o          ( s_timer3_apb_th_hi    ),
		.timer3_th_low_o         ( s_timer3_apb_th_low   ),
		.timer3_ch0_mode_o       ( s_timer3_apb_ch0_mode ),
		.timer3_ch0_flt_o        ( s_timer3_apb_ch0_flt  ),
		.timer3_ch0_th_o         ( s_timer3_apb_ch0_th   ),
		.timer3_ch0_lut_o        ( s_timer3_apb_ch0_lut  ),
		.timer3_ch1_mode_o       ( s_timer3_apb_ch1_mode ),
		.timer3_ch1_flt_o        ( s_timer3_apb_ch1_flt  ),
		.timer3_ch1_th_o         ( s_timer3_apb_ch1_th   ),
		.timer3_ch1_lut_o        ( s_timer3_apb_ch1_lut  ),
		.timer3_ch2_mode_o       ( s_timer3_apb_ch2_mode ),
		.timer3_ch2_flt_o        ( s_timer3_apb_ch2_flt  ),
		.timer3_ch2_th_o         ( s_timer3_apb_ch2_th   ),
		.timer3_ch2_lut_o        ( s_timer3_apb_ch2_lut  ),
		.timer3_ch3_mode_o       ( s_timer3_apb_ch3_mode ),
		.timer3_ch3_flt_o        ( s_timer3_apb_ch3_flt  ),
		.timer3_ch3_th_o         ( s_timer3_apb_ch3_th   ),
		.timer3_ch3_lut_o        ( s_timer3_apb_ch3_lut  ),
		.timer3_counter_i        ( s_timer3_counter      ),
		
		.timer0_clk_en_o         ( s_timer0_clk_en       ),
		.timer1_clk_en_o         ( s_timer1_clk_en       ),
		.timer2_clk_en_o         ( s_timer2_clk_en       ),
		.timer3_clk_en_o         ( s_timer3_clk_en       )
	);

/////////////////////////////////////////////////////////////////////
//
//         TIMER0
//
/////////////////////////////////////////////////////////////////////
	logic [7:0] s_timer0_status;
	logic [N_TIMEREXTSIG-1:0] s_timer0_signal;
	assign s_timer0_signal = {ch_3_o,ch_2_o,ch_1_o,ch_0_o,ext_sig_i};

    pulp_clock_gating i_clk_gate_timer0
    (
        .clk_i(HCLK),
        .en_i(s_timer0_clk_en),
        .test_en_i(dft_cg_enable_i),
        .clk_o(s_clk_timer0)
    );

	timer_module #(
		.NUM_BITS(TIMER_NBITS),
		.N_EXTSIG(N_TIMEREXTSIG)
	) u_tim0 (
		.clk_i            ( s_clk_timer0          ),
		.rstn_i           ( HRESETn               ),

		.cfg_start_i      ( s_timer0_apb_start    ),
		.cfg_stop_i       ( s_timer0_apb_stop     ),
		.cfg_rst_i        ( s_timer0_apb_rst      ),
		.cfg_update_i     ( s_timer0_apb_update   ),
		.cfg_arm_i        ( s_timer0_apb_arm      ),

		.cfg_sel_i        ( s_timer0_apb_in_sel   ),
		.cfg_sel_clk_i    ( s_timer0_apb_in_clk   ),
		.cfg_mode_i       ( s_timer0_apb_in_mode  ),

		.cfg_presc_i      ( s_timer0_apb_presc    ),

		.cfg_sawtooth_i   ( s_timer0_apb_saw      ),
		.cfg_cnt_start_i  ( s_timer0_apb_th_low   ),
		.cfg_cnt_end_i    ( s_timer0_apb_th_hi    ),

		.cfg_comp_ch0_i   ( s_timer0_apb_ch0_th   ),
		.cfg_comp_op_ch0_i( s_timer0_apb_ch0_mode ),
		.cfg_comp_ch1_i   ( s_timer0_apb_ch1_th   ),
		.cfg_comp_op_ch1_i( s_timer0_apb_ch1_mode ),
		.cfg_comp_ch2_i   ( s_timer0_apb_ch2_th   ),
		.cfg_comp_op_ch2_i( s_timer0_apb_ch2_mode ),
		.cfg_comp_ch3_i   ( s_timer0_apb_ch3_th   ),
		.cfg_comp_op_ch3_i( s_timer0_apb_ch3_mode ),

		.ls_clk_i         ( low_speed_clk_i       ),
		.signal_i         ( s_timer0_signal       ),

		.counter_o        ( s_timer0_counter      ),

		.pwm_o            ( ch_0_o                ),
		.status_o         ( s_timer0_status       )
	);

/////////////////////////////////////////////////////////////////////
//
//         TIMER0
//
/////////////////////////////////////////////////////////////////////
	logic [7:0] s_timer1_status;
	logic [N_TIMEREXTSIG-1:0] s_timer1_signal;
	assign s_timer1_signal = {ch_3_o,ch_2_o,ch_1_o,ch_0_o,ext_sig_i};

    pulp_clock_gating i_clk_gate_timer1
    (
        .clk_i(HCLK),
        .en_i(s_timer1_clk_en),
        .test_en_i(dft_cg_enable_i),
        .clk_o(s_clk_timer1)
    );

	timer_module #(
		.NUM_BITS(TIMER_NBITS),
		.N_EXTSIG(N_TIMEREXTSIG)
	) u_tim1 (
		.clk_i            ( s_clk_timer1          ),
		.rstn_i           ( HRESETn               ),

		.cfg_start_i      ( s_timer1_apb_start    ),
		.cfg_stop_i       ( s_timer1_apb_stop     ),
		.cfg_rst_i        ( s_timer1_apb_rst      ),
		.cfg_update_i     ( s_timer1_apb_update   ),
		.cfg_arm_i        ( s_timer1_apb_arm      ),

		.cfg_sel_i        ( s_timer1_apb_in_sel   ),
		.cfg_sel_clk_i    ( s_timer1_apb_in_clk   ),
		.cfg_mode_i       ( s_timer1_apb_in_mode  ),

		.cfg_presc_i      ( s_timer1_apb_presc    ),

		.cfg_sawtooth_i   ( s_timer1_apb_saw      ),
		.cfg_cnt_start_i  ( s_timer1_apb_th_low   ),
		.cfg_cnt_end_i    ( s_timer1_apb_th_hi    ),

		.cfg_comp_ch0_i   ( s_timer1_apb_ch0_th   ),
		.cfg_comp_op_ch0_i( s_timer1_apb_ch0_mode ),
		.cfg_comp_ch1_i   ( s_timer1_apb_ch1_th   ),
		.cfg_comp_op_ch1_i( s_timer1_apb_ch1_mode ),
		.cfg_comp_ch2_i   ( s_timer1_apb_ch2_th   ),
		.cfg_comp_op_ch2_i( s_timer1_apb_ch2_mode ),
		.cfg_comp_ch3_i   ( s_timer1_apb_ch3_th   ),
		.cfg_comp_op_ch3_i( s_timer1_apb_ch3_mode ),

		.ls_clk_i         ( low_speed_clk_i       ),
		.signal_i         ( s_timer1_signal       ),

		.counter_o        ( s_timer1_counter      ),

		.pwm_o            ( ch_1_o                ),
		.status_o         ( s_timer1_status       )
	);

/////////////////////////////////////////////////////////////////////
//
//         TIMER2
//
/////////////////////////////////////////////////////////////////////
	logic [7:0] s_timer2_status;
	logic [N_TIMEREXTSIG-1:0] s_timer2_signal;
	assign s_timer2_signal = {ch_3_o,ch_2_o,ch_1_o,ch_0_o,ext_sig_i};

    pulp_clock_gating i_clk_gate_timer2
    (
        .clk_i(HCLK),
        .en_i(s_timer2_clk_en),
        .test_en_i(dft_cg_enable_i),
        .clk_o(s_clk_timer2)
    );

	timer_module #(
		.NUM_BITS(TIMER_NBITS),
		.N_EXTSIG(N_TIMEREXTSIG)
	) u_tim2 (
		.clk_i            ( s_clk_timer2          ),
		.rstn_i           ( HRESETn               ),

		.cfg_start_i      ( s_timer2_apb_start    ),
		.cfg_stop_i       ( s_timer2_apb_stop     ),
		.cfg_rst_i        ( s_timer2_apb_rst      ),
		.cfg_update_i     ( s_timer2_apb_update   ),
		.cfg_arm_i        ( s_timer2_apb_arm      ),

		.cfg_sel_i        ( s_timer2_apb_in_sel   ),
		.cfg_sel_clk_i    ( s_timer2_apb_in_clk   ),
		.cfg_mode_i       ( s_timer2_apb_in_mode  ),

		.cfg_presc_i      ( s_timer2_apb_presc    ),

		.cfg_sawtooth_i   ( s_timer2_apb_saw      ),
		.cfg_cnt_start_i  ( s_timer2_apb_th_low   ),
		.cfg_cnt_end_i    ( s_timer2_apb_th_hi    ),

		.cfg_comp_ch0_i   ( s_timer2_apb_ch0_th   ),
		.cfg_comp_op_ch0_i( s_timer2_apb_ch0_mode ),
		.cfg_comp_ch1_i   ( s_timer2_apb_ch1_th   ),
		.cfg_comp_op_ch1_i( s_timer2_apb_ch1_mode ),
		.cfg_comp_ch2_i   ( s_timer2_apb_ch2_th   ),
		.cfg_comp_op_ch2_i( s_timer2_apb_ch2_mode ),
		.cfg_comp_ch3_i   ( s_timer2_apb_ch3_th   ),
		.cfg_comp_op_ch3_i( s_timer2_apb_ch3_mode ),

		.ls_clk_i         ( low_speed_clk_i       ),
		.signal_i         ( s_timer2_signal       ),

		.counter_o        ( s_timer2_counter      ),

		.pwm_o            ( ch_2_o                ),
		.status_o         ( s_timer2_status       )
	);

/////////////////////////////////////////////////////////////////////
//
//         TIMER3
//
/////////////////////////////////////////////////////////////////////
	logic [7:0] s_timer3_status;
	logic [N_TIMEREXTSIG-1:0] s_timer3_signal;
	assign s_timer3_signal = {ch_3_o,ch_2_o,ch_1_o,ch_0_o,ext_sig_i};

    pulp_clock_gating i_clk_gate_timer3
    (
        .clk_i(HCLK),
        .en_i(s_timer3_clk_en),
        .test_en_i(dft_cg_enable_i),
        .clk_o(s_clk_timer3)
    );

	timer_module #(
		.NUM_BITS(TIMER_NBITS),
		.N_EXTSIG(N_TIMEREXTSIG)
	) u_tim3 (
		.clk_i            ( s_clk_timer3          ),
		.rstn_i           ( HRESETn               ),

		.cfg_start_i      ( s_timer3_apb_start    ),
		.cfg_stop_i       ( s_timer3_apb_stop     ),
		.cfg_rst_i        ( s_timer3_apb_rst      ),
		.cfg_update_i     ( s_timer3_apb_update   ),
		.cfg_arm_i        ( s_timer3_apb_arm      ),

		.cfg_sel_i        ( s_timer3_apb_in_sel   ),
		.cfg_sel_clk_i    ( s_timer3_apb_in_clk   ),
		.cfg_mode_i       ( s_timer3_apb_in_mode  ),

		.cfg_presc_i      ( s_timer3_apb_presc    ),

		.cfg_sawtooth_i   ( s_timer3_apb_saw      ),
		.cfg_cnt_start_i  ( s_timer3_apb_th_low   ),
		.cfg_cnt_end_i    ( s_timer3_apb_th_hi    ),

		.cfg_comp_ch0_i   ( s_timer3_apb_ch0_th   ),
		.cfg_comp_op_ch0_i( s_timer3_apb_ch0_mode ),
		.cfg_comp_ch1_i   ( s_timer3_apb_ch1_th   ),
		.cfg_comp_op_ch1_i( s_timer3_apb_ch1_mode ),
		.cfg_comp_ch2_i   ( s_timer3_apb_ch2_th   ),
		.cfg_comp_op_ch2_i( s_timer3_apb_ch2_mode ),
		.cfg_comp_ch3_i   ( s_timer3_apb_ch3_th   ),
		.cfg_comp_op_ch3_i( s_timer3_apb_ch3_mode ),

		.ls_clk_i         ( low_speed_clk_i       ),
		.signal_i         ( s_timer3_signal       ),

		.counter_o        ( s_timer3_counter      ),

		.pwm_o            ( ch_3_o                ),
		.status_o         ( s_timer3_status       )
	);

	logic [15:0] s_event_signals;
	logic  [1:0] r_event_sync_0;
	logic  [1:0] r_event_sync_1;
	logic  [1:0] r_event_sync_2;
	logic  [1:0] r_event_sync_3;

	assign s_event_signals = {ch_3_o,ch_2_o,ch_1_o,ch_0_o};

	assign events_o[0] = s_event_en[0] & r_event_sync_0[1] & ~r_event_sync_0[0];
	assign events_o[1] = s_event_en[1] & r_event_sync_1[1] & ~r_event_sync_1[0];
	assign events_o[2] = s_event_en[2] & r_event_sync_2[1] & ~r_event_sync_2[0];
	assign events_o[3] = s_event_en[3] & r_event_sync_3[1] & ~r_event_sync_3[0];

	always_ff @(posedge HCLK or negedge HRESETn) begin : proc_edgedet
		if(~HRESETn) begin
			r_event_sync_0 <= 0;
			r_event_sync_1 <= 0;
			r_event_sync_2 <= 0;
			r_event_sync_3 <= 0;
		end else begin
			if (s_event_en[0])
				r_event_sync_0 <= {s_event_signals[s_event_sel_0],r_event_sync_0[1]};
			if (s_event_en[1])
				r_event_sync_1 <= {s_event_signals[s_event_sel_1],r_event_sync_1[1]};
			if (s_event_en[2])
				r_event_sync_2 <= {s_event_signals[s_event_sel_2],r_event_sync_2[1]};
			if (s_event_en[3])
				r_event_sync_3 <= {s_event_signals[s_event_sel_3],r_event_sync_3[1]};
		end
	end

endmodule // apb_adv_timer
